<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>冒泡排序动画演示</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#3B82F6',
                        secondary: '#10B981',
                        accent: '#F59E0B',
                        highlight: '#EF4444',
                        dark: '#1E293B',
                        light: '#F8FAFC'
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .animation-controls {
                @apply flex items-center gap-3 p-4 bg-dark/5 rounded-lg;
            }
            .btn-control {
                @apply px-4 py-2 rounded-lg transition-all duration-300 font-medium text-sm;
            }
            .btn-control-primary {
                @apply btn-control bg-primary hover:bg-primary/80 text-white;
            }
            .btn-control-secondary {
                @apply btn-control bg-secondary hover:bg-secondary/80 text-white;
            }
            .btn-control-outline {
                @apply btn-control border border-dark/20 hover:bg-dark/5 text-dark;
            }
            .sort-container {
                @apply relative h-[400px] w-full bg-light/50 rounded-lg overflow-hidden;
            }
            .bar {
                @apply absolute bottom-0 transition-all duration-300 origin-bottom;
            }
            .bar-number {
                @apply absolute top-0 left-1/2 -translate-x-1/2 -translate-y-full text-xs font-medium;
            }
            .stats-card {
                @apply bg-white rounded-lg p-4 shadow-sm border border-gray-100;
            }
            .stats-value {
                @apply text-2xl font-bold text-dark;
            }
            .stats-label {
                @apply text-sm text-gray-500;
            }
        }
    </style>
</head>
<body class="bg-gray-50 font-sans text-dark min-h-screen">
    <div class="container mx-auto px-4 py-8 max-w-4xl">
        <header class="mb-8 text-center">
            <h1 class="text-[clamp(1.75rem,4vw,2.5rem)] font-bold text-dark mb-2">冒泡排序可视化演示</h1>
            <p class="text-gray-600 max-w-2xl mx-auto">
                冒泡排序是一种简单的排序算法，它重复地走访过要排序的数列，一次比较两个元素，如果它们的顺序错误就把它们交换过来。
                通过这个可视化工具，你可以直观地理解冒泡排序的工作原理。
            </p>
        </header>

        <main class="space-y-6">
            <!-- 排序可视化区域 -->
            <div class="sort-container">
                <div id="bars-container" class="absolute bottom-0 left-0 right-0 h-full flex items-end justify-center gap-1 p-2"></div>
            </div>

            <!-- 控制面板 -->
            <div class="animation-controls">
                <button id="generate-btn" class="btn-control-primary">
                    <i class="fa fa-refresh mr-2"></i>生成新数组
                </button>
                <button id="start-btn" class="btn-control-secondary">
                    <i class="fa fa-play mr-2"></i>开始排序
                </button>
                <button id="pause-btn" class="btn-control-outline" disabled>
                    <i class="fa fa-pause mr-2"></i>暂停
                </button>
                <button id="reset-btn" class="btn-control-outline" disabled>
                    <i class="fa fa-undo mr-2"></i>重置
                </button>
                
                <div class="ml-auto flex items-center gap-2">
                    <label for="speed-slider" class="text-sm text-gray-600">速度:</label>
                    <input type="range" id="speed-slider" min="10" max="500" value="200" 
                           class="w-32 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
                    <span id="speed-value" class="text-sm text-gray-600">200ms</span>
                </div>
            </div>

            <!-- 统计信息 -->
            <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
                <div class="stats-card">
                    <div class="stats-value" id="comparisons-count">0</div>
                    <div class="stats-label">比较次数</div>
                </div>
                <div class="stats-card">
                    <div class="stats-value" id="swaps-count">0</div>
                    <div class="stats-label">交换次数</div>
                </div>
                <div class="stats-card">
                    <div class="stats-value" id="array-size">20</div>
                    <div class="stats-label">数组大小</div>
                </div>
            </div>

            <!-- 算法说明 -->
            <div class="bg-white rounded-lg p-6 shadow-sm border border-gray-100">
                <h2 class="text-xl font-semibold mb-3">冒泡排序原理</h2>
                <p class="text-gray-700 mb-4">
                    冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列，一次比较两个元素，如果它们的顺序错误就把它们交换过来。
                    走访数列的工作是重复地进行直到没有再需要交换，也就是说该数列已经排序完成。
                </p>
                <h3 class="text-lg font-medium mb-2">算法步骤：</h3>
                <ol class="list-decimal list-inside text-gray-700 space-y-1">
                    <li>比较相邻的元素。如果第一个比第二个大，就把它们交换过来。</li>
                    <li>对每一对相邻元素作同样的工作，从开始第一对到结尾的最后一对。这步做完后，最后的元素会是最大的数。</li>
                    <li>针对所有的元素重复以上的步骤，除了最后一个。</li>
                    <li>持续每次对越来越少的元素重复上面的步骤，直到没有任何一对数字需要比较。</li>
                </ol>
                <div class="mt-4 bg-gray-50 p-4 rounded-lg">
                    <pre class="text-sm text-gray-800 overflow-x-auto"><code>function bubbleSort(arr) {
    const n = arr.length;
    for (let i = 0; i < n-1; i++) {
        for (let j = 0; j < n-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                // 交换 arr[j] 和 arr[j+1]
                let temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
    return arr;
}</code></pre>
                </div>
            </div>
        </main>

        <footer class="mt-12 text-center text-gray-500 text-sm">
            <p>冒泡排序可视化演示工具 &copy; 2025</p>
        </footer>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 常量和变量
            const container = document.getElementById('bars-container');
            const generateBtn = document.getElementById('generate-btn');
            const startBtn = document.getElementById('start-btn');
            const pauseBtn = document.getElementById('pause-btn');
            const resetBtn = document.getElementById('reset-btn');
            const speedSlider = document.getElementById('speed-slider');
            const speedValue = document.getElementById('speed-value');
            const comparisonsCount = document.getElementById('comparisons-count');
            const swapsCount = document.getElementById('swaps-count');
            const arraySize = document.getElementById('array-size');
            
            let array = [];
            let arraySizeValue = 20;
            let animationSpeed = 200; // 毫秒
            let animationId = null;
            let isSorting = false;
            let isPaused = false;
            let animationSteps = [];
            let currentStep = 0;
            let comparisons = 0;
            let swaps = 0;
            
            // 初始化
            init();
            
            function init() {
                generateRandomArray();
                renderBars();
                updateControls();
                updateStats();
                
                // 事件监听
                generateBtn.addEventListener('click', generateRandomArray);
                startBtn.addEventListener('click', startSorting);
                pauseBtn.addEventListener('click', togglePause);
                resetBtn.addEventListener('click', resetSorting);
                speedSlider.addEventListener('input', updateSpeed);
            }
            
            // 生成随机数组
            function generateRandomArray() {
                stopSorting();
                array = [];
                for (let i = 0; i < arraySizeValue; i++) {
                    array.push(Math.floor(Math.random() * 90) + 10);
                }
                renderBars();
                comparisons = 0;
                swaps = 0;
                updateStats();
            }
            
            // 渲染柱状图
            function renderBars(comparingIndices = [], sortedIndices = []) {
                container.innerHTML = '';
                const barWidth = (container.offsetWidth - (array.length - 1)) / array.length;
                
                array.forEach((value, index) => {
                    const bar = document.createElement('div');
                    bar.className = 'bar';
                    bar.style.width = `${barWidth}px`;
                    bar.style.height = `${value * 3}px`;
                    bar.style.left = `${index * (barWidth + 1)}px`;
                    
                    // 设置默认颜色
                    let bgColor = 'bg-primary';
                    
                    // 高亮正在比较的柱子
                    if (comparingIndices.includes(index)) {
                        bgColor = 'bg-highlight';
                    } 
                    // 标记已排序的柱子
                    else if (sortedIndices.includes(index)) {
                        bgColor = 'bg-secondary';
                    }
                    
                    bar.classList.add(bgColor);
                    
                    // 添加数值标签
                    const numberLabel = document.createElement('div');
                    numberLabel.className = 'bar-number';
                    numberLabel.textContent = value;
                    bar.appendChild(numberLabel);
                    
                    container.appendChild(bar);
                });
            }
            
            // 生成冒泡排序的所有步骤
            function generateSortSteps() {
                animationSteps = [];
                const arrCopy = [...array];
                const n = arrCopy.length;
                
                for (let i = 0; i < n - 1; i++) {
                    for (let j = 0; j < n - i - 1; j++) {
                        // 记录比较步骤
                        animationSteps.push({
                            type: 'compare',
                            indices: [j, j + 1],
                            array: [...arrCopy],
                            comparisons: comparisons + 1,
                            swaps
                        });
                        
                        comparisons++;
                        
                        if (arrCopy[j] > arrCopy[j + 1]) {
                            // 交换元素
                            [arrCopy[j], arrCopy[j + 1]] = [arrCopy[j + 1], arrCopy[j]];
                            
                            // 记录交换步骤
                            animationSteps.push({
                                type: 'swap',
                                indices: [j, j + 1],
                                array: [...arrCopy],
                                comparisons,
                                swaps: swaps + 1
                            });
                            
                            swaps++;
                        }
                    }
                    
                    // 标记已排序的元素
                    animationSteps.push({
                        type: 'sorted',
                        indices: [],
                        sortedIndices: Array.from({length: i + 1}, (_, k) => n - k - 1),
                        array: [...arrCopy],
                        comparisons,
                        swaps
                    });
                }
                
                // 标记所有元素为已排序
                animationSteps.push({
                    type: 'complete',
                    indices: [],
                    sortedIndices: Array.from({length: n}, (_, k) => k),
                    array: [...arrCopy],
                    comparisons,
                    swaps
                });
            }
            
            // 执行排序动画
            function animateSort() {
                if (currentStep >= animationSteps.length) {
                    isSorting = false;
                    updateControls();
                    return;
                }
                
                const step = animationSteps[currentStep];
                array = step.array;
                comparisons = step.comparisons;
                swaps = step.swaps;
                
                if (step.type === 'compare' || step.type === 'swap') {
                    renderBars(step.indices, step.sortedIndices || []);
                } else {
                    renderBars([], step.sortedIndices);
                }
                
                updateStats();
                
                currentStep++;
                animationId = setTimeout(animateSort, animationSpeed);
            }
            
            // 开始排序
            function startSorting() {
                if (isSorting) return;
                
                if (currentStep === 0) {
                    generateSortSteps();
                }
                
                isSorting = true;
                isPaused = false;
                animateSort();
                updateControls();
            }
            
            // 暂停/继续排序
            function togglePause() {
                if (isPaused) {
                    // 继续排序
                    isPaused = false;
                    animateSort();
                    pauseBtn.innerHTML = '<i class="fa fa-pause mr-2"></i>暂停';
                } else {
                    // 暂停排序
                    isPaused = true;
                    clearTimeout(animationId);
                    pauseBtn.innerHTML = '<i class="fa fa-play mr-2"></i>继续';
                }
            }
            
            // 停止排序
            function stopSorting() {
                isSorting = false;
                isPaused = false;
                clearTimeout(animationId);
                updateControls();
            }
            
            // 重置排序
            function resetSorting() {
                stopSorting();
                currentStep = 0;
                renderBars();
                comparisons = 0;
                swaps = 0;
                updateStats();
            }
            
            // 更新速度
            function updateSpeed() {
                animationSpeed = 510 - speedSlider.value; // 反转值，使滑块右侧更快
                speedValue.textContent = `${animationSpeed}ms`;
                
                if (isPaused) {
                    // 如果暂停，更新按钮上的速度显示
                    pauseBtn.innerHTML = `<i class="fa fa-play mr-2"></i>继续 (${animationSpeed}ms)`;
                }
            }
            
            // 更新控制按钮状态
            function updateControls() {
                startBtn.disabled = isSorting;
                pauseBtn.disabled = !isSorting;
                resetBtn.disabled = !isSorting && currentStep === 0;
                
                if (isPaused) {
                    pauseBtn.innerHTML = '<i class="fa fa-play mr-2"></i>继续';
                } else {
                    pauseBtn.innerHTML = '<i class="fa fa-pause mr-2"></i>暂停';
                }
            }
            
            // 更新统计信息
            function updateStats() {
                comparisonsCount.textContent = comparisons;
                swapsCount.textContent = swaps;
                arraySize.textContent = arraySizeValue;
            }
            
            // 窗口大小变化时重新渲染
            window.addEventListener('resize', renderBars);
        });
    </script>
</body>
</html>